Skip to content

Add --force-refresh flag to CLI token source#1604

Open
mihaimitrea-db wants to merge 1 commit intomainfrom
mihaimitrea-db/stack/force-refresh-flag
Open

Add --force-refresh flag to CLI token source#1604
mihaimitrea-db wants to merge 1 commit intomainfrom
mihaimitrea-db/stack/force-refresh-flag

Conversation

@mihaimitrea-db
Copy link
Copy Markdown
Contributor

@mihaimitrea-db mihaimitrea-db commented Mar 30, 2026

🥞 Stacked PR

Use this link to review incremental changes.


Summary

Pass --force-refresh to the Databricks CLI auth token command so the SDK always receives a fresh token instead of a potentially stale one from the CLI's internal cache.

Why

The SDK manages its own token caching via CachedTokenSource. When the SDK decides it needs a new token and shells out to databricks auth token, the CLI may return a cached token that is about to expire (or has already expired from the SDK's perspective). This creates unnecessary refresh failures and retry loops.

The CLI recently added a --force-refresh flag (databricks/cli#4767) that bypasses its internal cache. By using this flag, the SDK is guaranteed a freshly minted token every time it asks for one, eliminating the stale-token problem.

What changed

Interface changes

None. CliTokenSource is not part of the public API surface.

Behavioral changes

  • The SDK now passes --force-refresh when invoking databricks auth token. When a profile is configured, --force-refresh is appended to the --profile command. When only a host is configured, --force-refresh is appended to the --host command instead.
  • If the CLI is too old to support --force-refresh, the SDK falls back to the plain --profile or --host command (and then to --host if --profile is also unsupported).
  • Warning messages are logged when falling back:
    • "Databricks CLI does not support --force-refresh flag. The CLI's token cache may provide stale tokens. Please upgrade your CLI to the latest version."
    • "Databricks CLI does not support --profile flag. Falling back to --host. Please upgrade your CLI to the latest version."

Internal changes

  • CliTokenSource now holds three command variants: forceCmd (--force-refresh), profileCmd (--profile), and hostCmd (--host). Any of these can be nil depending on the config.
  • buildCliCommands returns all three commands. forceCmd is based on profileCmd when a profile is set, or on hostCmd when only a host is configured — so host-only setups also benefit from cache bypass.
  • Token() tries commands in order (forceCmdprofileCmdhostCmd), falling through on "unknown flag:" errors and logging a warning at each step.
  • isUnknownFlagError is now parameterized: pass a specific flag name to match against the CLI's error output.
  • execCliCommand uses %q for stderr formatting so multi-line CLI output (usage text on unknown-flag errors) stays on one line in logs.

How is this tested?

Unit tests in config/cli_token_source_test.go:

  • TestBuildCliCommands — verifies all three commands are built correctly for each config combination (host-only, account host, profile+host, profile-only), including force-refresh based on host for host-only configs.
  • TestCliTokenSource_Token_Fallback — table-driven test covering:
    • forceCmd succeeds (happy path)
    • forceCmd fails → fallback to profileCmd
    • forceCmd fails → fallback to hostCmd (no profile configured)
    • profileCmd fails → fallback to hostCmd
    • Full fallback chain: forceCmdprofileCmdhostCmd
    • Real (non-flag) error stops fallback immediately
    • All commands fail with unknown flag — last error returned

@mihaimitrea-db
Copy link
Copy Markdown
Contributor Author

Range-diff: main (67f79d8 -> db4df21)
config/cli_token_source.go
@@ -95,14 +95,11 @@
 +			return nil, err
 +		}
  		logger.Warnf(ctx, "Databricks CLI does not support --profile flag. Falling back to --host. Please upgrade your CLI to the latest version.")
-+	}
-+
-+	if c.hostCmd != nil {
- 		return c.execCliCommand(ctx, c.hostCmd)
+-		return c.execCliCommand(ctx, c.hostCmd)
  	}
 -	return tok, err
 +
-+	return nil, fmt.Errorf("no CLI command configured")
++	return c.execCliCommand(ctx, c.hostCmd)
  }
  
  func (c *CliTokenSource) execCliCommand(ctx context.Context, args []string) (*oauth2.Token, error) {

Reproduce locally: git range-diff 78469e6..67f79d8 78469e6..db4df21 | Disable: git config gitstack.push-range-diff false

@mihaimitrea-db mihaimitrea-db force-pushed the mihaimitrea-db/stack/force-refresh-flag branch from db4df21 to 97a1007 Compare March 30, 2026 15:14
@mihaimitrea-db
Copy link
Copy Markdown
Contributor Author

Range-diff: main (db4df21 -> 97a1007)
config/cli_token_source.go
@@ -80,7 +80,7 @@
 +		if err == nil {
 +			return tok, nil
 +		}
-+		if !isUnknownFlagError(err, "") {
++		if !isUnknownFlagError(err, "--force-refresh") && !isUnknownFlagError(err, "--profile") {
 +			return nil, err
 +		}
 +		logger.Warnf(ctx, "Databricks CLI does not support --force-refresh flag. The CLI's token cache may provide stale tokens. Please upgrade your CLI to the latest version.")
@@ -99,6 +99,9 @@
  	}
 -	return tok, err
 +
++	if c.hostCmd == nil {
++		return nil, fmt.Errorf("cannot get access token: no CLI commands available")
++	}
 +	return c.execCliCommand(ctx, c.hostCmd)
  }
  
config/cli_token_source_test.go
@@ -98,12 +98,13 @@
  
 -func TestCliTokenSource_Token_FallbackOnUnknownFlag(t *testing.T) {
 +func TestCliTokenSource_Token_ForceRefreshFallbackToProfile(t *testing.T) {
-+	if runtime.GOOS == "windows" {
-+		t.Skip("Skipping shell script test on Windows")
-+	}
-+
-+	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
-+	validResponse, _ := json.Marshal(cliTokenResponse{
+ 	if runtime.GOOS == "windows" {
+ 		t.Skip("Skipping shell script test on Windows")
+ 	}
+ 
+ 	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
+ 	validResponse, _ := json.Marshal(cliTokenResponse{
+-		AccessToken: "fallback-token",
 +		AccessToken: "profile-token",
 +		TokenType:   "Bearer",
 +		Expiry:      expiry,
@@ -135,13 +136,12 @@
 +}
 +
 +func TestCliTokenSource_Token_ProfileFallbackToHost(t *testing.T) {
- 	if runtime.GOOS == "windows" {
- 		t.Skip("Skipping shell script test on Windows")
- 	}
- 
- 	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
- 	validResponse, _ := json.Marshal(cliTokenResponse{
--		AccessToken: "fallback-token",
++	if runtime.GOOS == "windows" {
++		t.Skip("Skipping shell script test on Windows")
++	}
++
++	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
++	validResponse, _ := json.Marshal(cliTokenResponse{
 +		AccessToken: "host-token",
  		TokenType:   "Bearer",
  		Expiry:      expiry,
@@ -255,4 +255,37 @@
 +		hostCmd:    []string{hostScript},
  	}
  	_, err := ts.Token(context.Background())
- 	if err == nil {
\ No newline at end of file
+ 	if err == nil {
+ 		t.Errorf("Token() error = %v, want error containing original auth failure", err)
+ 	}
+ }
++
++func TestCliTokenSource_Token_NilHostCmdReturnsError(t *testing.T) {
++	if runtime.GOOS == "windows" {
++		t.Skip("Skipping shell script test on Windows")
++	}
++
++	tempDir := t.TempDir()
++
++	forceScript := filepath.Join(tempDir, "force_cli.sh")
++	if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
++		t.Fatalf("failed to create force script: %v", err)
++	}
++
++	profileScript := filepath.Join(tempDir, "profile_cli.sh")
++	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
++		t.Fatalf("failed to create profile script: %v", err)
++	}
++
++	ts := &CliTokenSource{
++		forceCmd:   []string{forceScript},
++		profileCmd: []string{profileScript},
++	}
++	_, err := ts.Token(context.Background())
++	if err == nil {
++		t.Fatal("Token() error = nil, want error")
++	}
++	if !strings.Contains(err.Error(), "no CLI commands available") {
++		t.Errorf("Token() error = %v, want error containing %q", err, "no CLI commands available")
++	}
++}
\ No newline at end of file

Reproduce locally: git range-diff 78469e6..db4df21 78469e6..97a1007 | Disable: git config gitstack.push-range-diff false

@mihaimitrea-db mihaimitrea-db force-pushed the mihaimitrea-db/stack/force-refresh-flag branch from 97a1007 to 69a7c95 Compare March 30, 2026 16:27
@mihaimitrea-db
Copy link
Copy Markdown
Contributor Author

Range-diff: main (97a1007 -> 69a7c95)
config/cli_token_source.go
@@ -106,6 +106,14 @@
  }
  
  func (c *CliTokenSource) execCliCommand(ctx context.Context, args []string) (*oauth2.Token, error) {
+ 	if err != nil {
+ 		var exitErr *exec.ExitError
+ 		if errors.As(err, &exitErr) {
+-			return nil, fmt.Errorf("cannot get access token: %s", strings.TrimSpace(string(exitErr.Stderr)))
++			return nil, fmt.Errorf("cannot get access token: %q", strings.TrimSpace(string(exitErr.Stderr)))
+ 		}
+ 		return nil, fmt.Errorf("cannot get access token: %w", err)
+ 	}
  }
  
  // isUnknownFlagError returns true if the error indicates the CLI does not

Reproduce locally: git range-diff 78469e6..97a1007 78469e6..69a7c95 | Disable: git config gitstack.push-range-diff false

Comment on lines 38 to 47
// forceCmd uses --profile with --force-refresh to bypass the CLI's token cache.
// Nil when cfg.Profile is empty (--force-refresh requires --profile support).
forceCmd []string

// hostCmd is a fallback command using --host, used when the primary --profile
// command fails because the CLI is too old to support --profile.
// profileCmd uses --profile for token lookup. Nil when cfg.Profile is empty.
profileCmd []string

// hostCmd uses --host as a fallback for CLIs that predate --profile support.
// Nil when cfg.Host is empty.
hostCmd []string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add maybe versions of the CLI in which each flag was added?

Comment on lines +70 to +72
if cfg.Host != "" {
hostCmd = buildHostCommand(cliPath, cfg)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about force refresh with the host set?

Comment on lines +326 to +451
@@ -327,21 +412,27 @@ func TestCliTokenSource_Token_NoFallbackOnRealError(t *testing.T) {

tempDir := t.TempDir()

// Primary script fails with a real auth error (not unknown flag).
// forceCmd fails with a real auth error (not unknown flag).
forceScript := filepath.Join(tempDir, "force_cli.sh")
if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'cache: databricks OAuth is not configured for this host' >&2\nexit 1"), 0755); err != nil {
t.Fatalf("failed to create force script: %v", err)
}

// profileCmd and hostCmd should not be called.
profileScript := filepath.Join(tempDir, "profile_cli.sh")
if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'cache: databricks OAuth is not configured for this host' >&2\nexit 1"), 0755); err != nil {
if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'should not reach here' >&2\nexit 1"), 0755); err != nil {
t.Fatalf("failed to create profile script: %v", err)
}

// Fallback script would succeed, but should not be called.
hostScript := filepath.Join(tempDir, "host_cli.sh")
if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho 'should not reach here' >&2\nexit 1"), 0755); err != nil {
t.Fatalf("failed to create host script: %v", err)
}

ts := &CliTokenSource{
cmd: []string{profileScript},
hostCmd: []string{hostScript},
forceCmd: []string{forceScript},
profileCmd: []string{profileScript},
hostCmd: []string{hostScript},
}
_, err := ts.Token(context.Background())
if err == nil {
@@ -351,3 +442,33 @@ func TestCliTokenSource_Token_NoFallbackOnRealError(t *testing.T) {
t.Errorf("Token() error = %v, want error containing original auth failure", err)
}
}

func TestCliTokenSource_Token_NilHostCmdReturnsError(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping shell script test on Windows")
}

tempDir := t.TempDir()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about righting them as table tests? Because they seem to be testing the same flow.


### New Features and Improvements

* Pass `--force-refresh` to Databricks CLI `auth token` command to bypass the CLI's internal token cache.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be an internal change, right? Because IIUC it was not broken before.

Comment on lines +148 to +150
if flag == "" {
return strings.Contains(err.Error(), "unknown flag:")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this condition?

Pass --force-refresh to the Databricks CLI auth token command to bypass
the CLI's internal token cache. The SDK manages its own token caching,
so the CLI serving stale tokens from its cache causes unnecessary
refresh failures.

Commands are now tried in order: forceCmd (--profile + --force-refresh),
profileCmd (--profile), hostCmd (--host), falling back progressively
for older CLI versions that don't support newer flags.

Signed-off-by: Mihai Mitrea <mihai.mitrea@databricks.com>
@mihaimitrea-db mihaimitrea-db force-pushed the mihaimitrea-db/stack/force-refresh-flag branch from 69a7c95 to a314000 Compare April 7, 2026 09:18
@mihaimitrea-db
Copy link
Copy Markdown
Contributor Author

Range-diff: main (69a7c95 -> a314000)
NEXT_CHANGELOG.md
@@ -2,9 +2,9 @@
 --- a/NEXT_CHANGELOG.md
 +++ b/NEXT_CHANGELOG.md
  
- ### New Features and Improvements
+ ### Internal Changes
  
-+* Pass `--force-refresh` to Databricks CLI `auth token` command to bypass the CLI's internal token cache.
- * Added `HostMetadataResolver` hook to allow callers to customize host metadata resolution, e.g. with caching ([#1572](https://github.com/databricks/databricks-sdk-go/pull/1572)).
- * Added `NewLimitIterator` to `listing` package for lazy iteration with a cap on output items ([#1555](https://github.com/databricks/databricks-sdk-go/pull/1555)).
- 
\ No newline at end of file
++ * Pass `--force-refresh` to Databricks CLI `auth token` command to bypass the CLI's internal token cache.
+  * Use resolved host type from host metadata in `HostType()` method, falling back to URL pattern matching when metadata is unavailable.
+  * Normalize internal token sources on `auth.TokenSource` for proper context propagation ([#1577](https://github.com/databricks/databricks-sdk-go/pull/1577)).
+  * Fix `TestAzureGithubOIDCCredentials` hang caused by missing `HTTPTransport` stub: `EnsureResolved` now calls `resolveHostMetadata`, which makes a real network request when no transport is set ([#1550](https://github.com/databricks/databricks-sdk-go/pull/1550)).
\ No newline at end of file
config/cli_token_source.go
@@ -10,13 +10,17 @@
  type CliTokenSource struct {
 -	// cmd is the primary command to execute (--profile when available, --host otherwise).
 -	cmd []string
-+	// forceCmd uses --profile with --force-refresh to bypass the CLI's token cache.
-+	// Nil when cfg.Profile is empty (--force-refresh requires --profile support).
-+	forceCmd []string
- 
+-
 -	// hostCmd is a fallback command using --host, used when the primary --profile
 -	// command fails because the CLI is too old to support --profile.
++	// forceCmd appends --force-refresh to the base command (profileCmd when a
++	// profile is configured, hostCmd otherwise) to bypass the CLI's token cache.
++	// Nil when neither profile nor host is set.
++	// CLI support: >= v0.296.0 (databricks/cli#4767).
++	forceCmd []string
++
 +	// profileCmd uses --profile for token lookup. Nil when cfg.Profile is empty.
++	// CLI support: >= v0.207.1 (databricks/cli#855).
 +	profileCmd []string
 +
 +	// hostCmd uses --host as a fallback for CLIs that predate --profile support.
@@ -39,10 +43,10 @@
 -// When cfg.Profile is empty, the primary command uses --host and no fallback
 -// is needed.
 -func buildCliCommands(cliPath string, cfg *Config) (primaryCmd []string, hostCmd []string) {
-+// When cfg.Profile is set, three commands are built: a --force-refresh variant,
-+// a plain --profile variant, and (when host is available) a --host fallback.
-+// When cfg.Profile is empty, only --host is returned — the CLI must support
-+// --profile before --force-refresh can be used (monotonic feature assumption).
++// When cfg.Profile is set, three commands are built: a --force-refresh variant
++// (based on profileCmd), a plain --profile variant, and (when host is available)
++// a --host fallback. When cfg.Profile is empty, --force-refresh is based on the
++// --host command instead.
 +func buildCliCommands(cliPath string, cfg *Config) ([]string, []string, []string) {
 +	var forceCmd, profileCmd, hostCmd []string
  	if cfg.Profile != "" {
@@ -53,12 +57,16 @@
 -		}
 -		return primary, nil
 +		profileCmd = []string{cliPath, "auth", "token", "--profile", cfg.Profile}
++	}
++	if cfg.Host != "" {
++		hostCmd = buildHostCommand(cliPath, cfg)
++	}
++	if profileCmd != nil {
 +		forceCmd = append(profileCmd, "--force-refresh")
++	} else if hostCmd != nil {
++		forceCmd = append(hostCmd, "--force-refresh")
  	}
 -	return buildHostCommand(cliPath, cfg), nil
-+	if cfg.Host != "" {
-+		hostCmd = buildHostCommand(cliPath, cfg)
-+	}
 +	return forceCmd, profileCmd, hostCmd
  }
  
@@ -121,12 +129,8 @@
 -// predate profile-based token lookup.
 -func isUnknownFlagError(err error) bool {
 -	return strings.Contains(err.Error(), "unknown flag: --profile")
-+// recognize a flag. Pass a specific flag (e.g. "--profile") to check for that
-+// flag, or pass "" to match any "unknown flag:" error.
++// recognize the given flag (e.g. "--profile", "--force-refresh").
 +func isUnknownFlagError(err error, flag string) bool {
-+	if flag == "" {
-+		return strings.Contains(err.Error(), "unknown flag:")
-+	}
 +	return strings.Contains(err.Error(), "unknown flag: "+flag)
  }
  
config/cli_token_source_test.go
@@ -18,17 +18,19 @@
 -			name:    "workspace host",
 -			cfg:     &Config{Host: host},
 -			wantCmd: []string{cliPath, "auth", "token", "--host", host},
-+			name:        "workspace host only",
-+			cfg:         &Config{Host: host},
-+			wantHostCmd: []string{cliPath, "auth", "token", "--host", host},
++			name:         "workspace host only — force-refresh based on host",
++			cfg:          &Config{Host: host},
++			wantForceCmd: []string{cliPath, "auth", "token", "--host", host, "--force-refresh"},
++			wantHostCmd:  []string{cliPath, "auth", "token", "--host", host},
  		},
  		{
 -			name:    "account host",
 -			cfg:     &Config{Host: accountHost, AccountID: accountID},
 -			wantCmd: []string{cliPath, "auth", "token", "--host", accountHost, "--account-id", accountID},
-+			name:        "account host only",
-+			cfg:         &Config{Host: accountHost, AccountID: accountID},
-+			wantHostCmd: []string{cliPath, "auth", "token", "--host", accountHost, "--account-id", accountID},
++			name:         "account host only — force-refresh based on host with account-id",
++			cfg:          &Config{Host: accountHost, AccountID: accountID},
++			wantForceCmd: []string{cliPath, "auth", "token", "--host", accountHost, "--account-id", accountID, "--force-refresh"},
++			wantHostCmd:  []string{cliPath, "auth", "token", "--host", accountHost, "--account-id", accountID},
  		},
  		{
  			name: "former unified host treated as workspace",
@@ -36,14 +38,15 @@
  				WorkspaceID: workspaceID,
  			},
 -			wantCmd: []string{cliPath, "auth", "token", "--host", unifiedHost},
-+			wantHostCmd: []string{cliPath, "auth", "token", "--host", unifiedHost},
++			wantForceCmd: []string{cliPath, "auth", "token", "--host", unifiedHost, "--force-refresh"},
++			wantHostCmd:  []string{cliPath, "auth", "token", "--host", unifiedHost},
  		},
  		{
 -			name:        "profile uses --profile with --host fallback",
 -			cfg:         &Config{Profile: "my-profile", Host: host},
 -			wantCmd:     []string{cliPath, "auth", "token", "--profile", "my-profile"},
 -			wantHostCmd: []string{cliPath, "auth", "token", "--host", host},
-+			name:           "profile with host — all three commands",
++			name:           "profile with host — force-refresh based on profile",
 +			cfg:            &Config{Profile: "my-profile", Host: host},
 +			wantForceCmd:   []string{cliPath, "auth", "token", "--profile", "my-profile", "--force-refresh"},
 +			wantProfileCmd: []string{cliPath, "auth", "token", "--profile", "my-profile"},
@@ -97,195 +100,172 @@
  }
  
 -func TestCliTokenSource_Token_FallbackOnUnknownFlag(t *testing.T) {
-+func TestCliTokenSource_Token_ForceRefreshFallbackToProfile(t *testing.T) {
++func TestCliTokenSource_Token_Fallback(t *testing.T) {
  	if runtime.GOOS == "windows" {
  		t.Skip("Skipping shell script test on Windows")
  	}
  
  	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
- 	validResponse, _ := json.Marshal(cliTokenResponse{
+-	validResponse, _ := json.Marshal(cliTokenResponse{
 -		AccessToken: "fallback-token",
-+		AccessToken: "profile-token",
-+		TokenType:   "Bearer",
-+		Expiry:      expiry,
-+	})
-+
-+	tempDir := t.TempDir()
-+
-+	forceScript := filepath.Join(tempDir, "force_cli.sh")
-+	if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --force-refresh' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create force script: %v", err)
-+	}
-+
-+	profileScript := filepath.Join(tempDir, "profile_cli.sh")
-+	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho '"+string(validResponse)+"'"), 0755); err != nil {
-+		t.Fatalf("failed to create profile script: %v", err)
-+	}
-+
-+	ts := &CliTokenSource{
-+		forceCmd:   []string{forceScript},
-+		profileCmd: []string{profileScript},
-+	}
-+	token, err := ts.Token(context.Background())
-+	if err != nil {
-+		t.Fatalf("Token() error = %v, want fallback to profileCmd to succeed", err)
-+	}
-+	if token.AccessToken != "profile-token" {
-+		t.Errorf("AccessToken = %q, want %q", token.AccessToken, "profile-token")
-+	}
-+}
-+
-+func TestCliTokenSource_Token_ProfileFallbackToHost(t *testing.T) {
-+	if runtime.GOOS == "windows" {
-+		t.Skip("Skipping shell script test on Windows")
-+	}
-+
-+	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
-+	validResponse, _ := json.Marshal(cliTokenResponse{
-+		AccessToken: "host-token",
++	successResponse, _ := json.Marshal(cliTokenResponse{
++		AccessToken: "test-token",
  		TokenType:   "Bearer",
  		Expiry:      expiry,
  	})
  
- 	tempDir := t.TempDir()
- 
+-	tempDir := t.TempDir()
+-
 -	// Primary script simulates an old CLI that doesn't know --profile.
- 	profileScript := filepath.Join(tempDir, "profile_cli.sh")
- 	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
- 		t.Fatalf("failed to create profile script: %v", err)
- 	}
- 
+-	profileScript := filepath.Join(tempDir, "profile_cli.sh")
+-	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
+-		t.Fatalf("failed to create profile script: %v", err)
+-	}
+-
 -	// Fallback script succeeds with --host.
- 	hostScript := filepath.Join(tempDir, "host_cli.sh")
- 	if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho '"+string(validResponse)+"'"), 0755); err != nil {
- 		t.Fatalf("failed to create host script: %v", err)
- 	}
+-	hostScript := filepath.Join(tempDir, "host_cli.sh")
+-	if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho '"+string(validResponse)+"'"), 0755); err != nil {
+-		t.Fatalf("failed to create host script: %v", err)
+-	}
++	success := "#!/bin/sh\necho '" + string(successResponse) + "'"
++	unknownForceRefresh := "#!/bin/sh\necho 'Error: unknown flag: --force-refresh' >&2\nexit 1"
++	unknownProfile := "#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"
++	realError := "#!/bin/sh\necho 'cache: databricks OAuth is not configured for this host' >&2\nexit 1"
++	unreachable := "#!/bin/sh\necho 'should not reach here' >&2\nexit 1"
  
- 	ts := &CliTokenSource{
+-	ts := &CliTokenSource{
 -		cmd:     []string{profileScript},
 -		hostCmd: []string{hostScript},
-+		profileCmd: []string{profileScript},
-+		hostCmd:    []string{hostScript},
- 	}
- 	token, err := ts.Token(context.Background())
- 	if err != nil {
+-	}
+-	token, err := ts.Token(context.Background())
+-	if err != nil {
 -		t.Fatalf("Token() error = %v, want fallback to succeed", err)
-+		t.Fatalf("Token() error = %v, want fallback to hostCmd to succeed", err)
- 	}
+-	}
 -	if token.AccessToken != "fallback-token" {
 -		t.Errorf("AccessToken = %q, want %q", token.AccessToken, "fallback-token")
-+	if token.AccessToken != "host-token" {
-+		t.Errorf("AccessToken = %q, want %q", token.AccessToken, "host-token")
-+	}
-+}
-+
-+func TestCliTokenSource_Token_ForceRefreshFallbackToHostOnProfileError(t *testing.T) {
-+	if runtime.GOOS == "windows" {
-+		t.Skip("Skipping shell script test on Windows")
-+	}
-+
-+	expiry := time.Now().Add(1 * time.Hour).Format(time.RFC3339)
-+	validResponse, _ := json.Marshal(cliTokenResponse{
-+		AccessToken: "host-token",
-+		TokenType:   "Bearer",
-+		Expiry:      expiry,
-+	})
-+
-+	tempDir := t.TempDir()
-+
-+	// forceCmd fails with --profile unknown (very old CLI).
-+	forceScript := filepath.Join(tempDir, "force_cli.sh")
-+	if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create force script: %v", err)
-+	}
-+
-+	// profileCmd also fails with --profile unknown.
-+	profileScript := filepath.Join(tempDir, "profile_cli.sh")
-+	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create profile script: %v", err)
-+	}
-+
-+	hostScript := filepath.Join(tempDir, "host_cli.sh")
-+	if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho '"+string(validResponse)+"'"), 0755); err != nil {
-+		t.Fatalf("failed to create host script: %v", err)
-+	}
-+
-+	ts := &CliTokenSource{
-+		forceCmd:   []string{forceScript},
-+		profileCmd: []string{profileScript},
-+		hostCmd:    []string{hostScript},
-+	}
-+	token, err := ts.Token(context.Background())
-+	if err != nil {
-+		t.Fatalf("Token() error = %v, want fallback through to hostCmd to succeed", err)
-+	}
-+	if token.AccessToken != "host-token" {
-+		t.Errorf("AccessToken = %q, want %q", token.AccessToken, "host-token")
+-	}
+-}
+-
+-func TestCliTokenSource_Token_NoFallbackOnRealError(t *testing.T) {
+-	if runtime.GOOS == "windows" {
+-		t.Skip("Skipping shell script test on Windows")
++	testCases := []struct {
++		name          string
++		forceScript   string // empty means nil forceCmd
++		profileScript string // empty means nil profileCmd
++		hostScript    string // empty means nil hostCmd
++		wantToken     string
++		wantErrMsg    string
++	}{
++		{
++			name:          "force-refresh succeeds",
++			forceScript:   success,
++			profileScript: unreachable,
++			hostScript:    unreachable,
++			wantToken:     "test-token",
++		},
++		{
++			name:          "force-refresh falls back to profile",
++			forceScript:   unknownForceRefresh,
++			profileScript: success,
++			wantToken:     "test-token",
++		},
++		{
++			name:        "force-refresh falls back to host (no profile)",
++			forceScript: unknownForceRefresh,
++			hostScript:  success,
++			wantToken:   "test-token",
++		},
++		{
++			name:          "profile falls back to host",
++			profileScript: unknownProfile,
++			hostScript:    success,
++			wantToken:     "test-token",
++		},
++		{
++			name:          "full fallback chain: force -> profile -> host",
++			forceScript:   unknownProfile,
++			profileScript: unknownProfile,
++			hostScript:    success,
++			wantToken:     "test-token",
++		},
++		{
++			name:          "real error stops fallback",
++			forceScript:   realError,
++			profileScript: unreachable,
++			hostScript:    unreachable,
++			wantErrMsg:    "databricks OAuth is not configured",
++		},
++		{
++			name:          "nil hostCmd after profile failure returns error",
++			forceScript:   unknownProfile,
++			profileScript: unknownProfile,
++			wantErrMsg:    "no CLI commands available",
++		},
  	}
- }
- 
  
- 	tempDir := t.TempDir()
- 
+-	tempDir := t.TempDir()
+-
 -	// Primary script fails with a real auth error (not unknown flag).
-+	// forceCmd fails with a real auth error (not unknown flag).
-+	forceScript := filepath.Join(tempDir, "force_cli.sh")
-+	if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'cache: databricks OAuth is not configured for this host' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create force script: %v", err)
-+	}
-+
-+	// profileCmd and hostCmd should not be called.
- 	profileScript := filepath.Join(tempDir, "profile_cli.sh")
+-	profileScript := filepath.Join(tempDir, "profile_cli.sh")
 -	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'cache: databricks OAuth is not configured for this host' >&2\nexit 1"), 0755); err != nil {
-+	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'should not reach here' >&2\nexit 1"), 0755); err != nil {
- 		t.Fatalf("failed to create profile script: %v", err)
- 	}
+-		t.Fatalf("failed to create profile script: %v", err)
+-	}
++	for _, tc := range testCases {
++		t.Run(tc.name, func(t *testing.T) {
++			tempDir := t.TempDir()
++			var ts CliTokenSource
  
 -	// Fallback script would succeed, but should not be called.
- 	hostScript := filepath.Join(tempDir, "host_cli.sh")
- 	if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho 'should not reach here' >&2\nexit 1"), 0755); err != nil {
- 		t.Fatalf("failed to create host script: %v", err)
- 	}
+-	hostScript := filepath.Join(tempDir, "host_cli.sh")
+-	if err := os.WriteFile(hostScript, []byte("#!/bin/sh\necho 'should not reach here' >&2\nexit 1"), 0755); err != nil {
+-		t.Fatalf("failed to create host script: %v", err)
+-	}
++			if tc.forceScript != "" {
++				path := filepath.Join(tempDir, "force_cli.sh")
++				if err := os.WriteFile(path, []byte(tc.forceScript), 0755); err != nil {
++					t.Fatalf("failed to create force script: %v", err)
++				}
++				ts.forceCmd = []string{path}
++			}
++			if tc.profileScript != "" {
++				path := filepath.Join(tempDir, "profile_cli.sh")
++				if err := os.WriteFile(path, []byte(tc.profileScript), 0755); err != nil {
++					t.Fatalf("failed to create profile script: %v", err)
++				}
++				ts.profileCmd = []string{path}
++			}
++			if tc.hostScript != "" {
++				path := filepath.Join(tempDir, "host_cli.sh")
++				if err := os.WriteFile(path, []byte(tc.hostScript), 0755); err != nil {
++					t.Fatalf("failed to create host script: %v", err)
++				}
++				ts.hostCmd = []string{path}
++			}
  
- 	ts := &CliTokenSource{
+-	ts := &CliTokenSource{
 -		cmd:     []string{profileScript},
 -		hostCmd: []string{hostScript},
-+		forceCmd:   []string{forceScript},
-+		profileCmd: []string{profileScript},
-+		hostCmd:    []string{hostScript},
- 	}
- 	_, err := ts.Token(context.Background())
- 	if err == nil {
- 		t.Errorf("Token() error = %v, want error containing original auth failure", err)
+-	}
+-	_, err := ts.Token(context.Background())
+-	if err == nil {
+-		t.Fatal("Token() error = nil, want error")
+-	}
+-	if !strings.Contains(err.Error(), "databricks OAuth is not configured") {
+-		t.Errorf("Token() error = %v, want error containing original auth failure", err)
++			token, err := ts.Token(context.Background())
++			if tc.wantErrMsg != "" {
++				if err == nil || !strings.Contains(err.Error(), tc.wantErrMsg) {
++					t.Errorf("Token() error = %v, want error containing %q", err, tc.wantErrMsg)
++				}
++				return
++			}
++			if err != nil {
++				t.Fatalf("Token() error = %v", err)
++			}
++			if token.AccessToken != tc.wantToken {
++				t.Errorf("AccessToken = %q, want %q", token.AccessToken, tc.wantToken)
++			}
++		})
  	}
- }
-+
-+func TestCliTokenSource_Token_NilHostCmdReturnsError(t *testing.T) {
-+	if runtime.GOOS == "windows" {
-+		t.Skip("Skipping shell script test on Windows")
-+	}
-+
-+	tempDir := t.TempDir()
-+
-+	forceScript := filepath.Join(tempDir, "force_cli.sh")
-+	if err := os.WriteFile(forceScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create force script: %v", err)
-+	}
-+
-+	profileScript := filepath.Join(tempDir, "profile_cli.sh")
-+	if err := os.WriteFile(profileScript, []byte("#!/bin/sh\necho 'Error: unknown flag: --profile' >&2\nexit 1"), 0755); err != nil {
-+		t.Fatalf("failed to create profile script: %v", err)
-+	}
-+
-+	ts := &CliTokenSource{
-+		forceCmd:   []string{forceScript},
-+		profileCmd: []string{profileScript},
-+	}
-+	_, err := ts.Token(context.Background())
-+	if err == nil {
-+		t.Fatal("Token() error = nil, want error")
-+	}
-+	if !strings.Contains(err.Error(), "no CLI commands available") {
-+		t.Errorf("Token() error = %v, want error containing %q", err, "no CLI commands available")
-+	}
-+}
\ No newline at end of file
+ }
\ No newline at end of file

Reproduce locally: git range-diff 78469e6..69a7c95 e068441..a314000 | Disable: git config gitstack.push-range-diff false

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

If integration tests don't run automatically, an authorized user can run them manually by following the instructions below:

Trigger:
go/deco-tests-run/sdk-go

Inputs:

  • PR number: 1604
  • Commit SHA: a3140009bed15894c45ba2d45f6769c307173f3f

Checks will be approved automatically on success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants